package com.bestv.search.engine.command;

import com.bestv.search.common.model.*;
import com.bestv.search.common.util.DateUtil;
import com.bestv.search.common.util.QueryStringProcess;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.compass.core.*;
import org.compass.core.CompassQueryBuilder.CompassBooleanQueryBuilder;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * This Class is search based on the Compass Framework.
 */
public class SearchCommand extends AbstractSearchCommand {
    protected final Log log = LogFactory.getLog(getClass());

    @SuppressWarnings({"unchecked", "rawtypes"})
    public String searchBySession() {
        return (String) getCompassTemplate().execute(new CompassCallback() {
            public String doInCompass(CompassSession session) throws CompassException {
                CompassHits hits = session.find("特工");
                return "{\"totalCount\":\"" + hits.length() + "\"}";
            }
        });
    }

    /**
     * Old search functions for vas demo based on JSON Search Method called from
     * DispatchSevlet which response to client
     *
     * @param jsonStr : a string follow JSON format
     * @return string follow JSON format
     * @throws JSONException json parse exception
     */
    public String search(String jsonStr) throws JSONException {
        List<String> keywords = new ArrayList<String>();
        List<String> fields = new ArrayList<String>();
        List<String> types = new ArrayList<String>();
        List<String> subtypes = new ArrayList<String>();

        String filter = null;

        int start = 0;
        int limit = 0;
        JSONObject jo = new JSONObject(jsonStr);
        JSONArray array;

        try {
            filter = jo.getString("filter");
        } catch (JSONException e) {
            log.error(e);
            filter = null;
        }

        try {
            start = jo.getInt("start");
            limit = jo.getInt("limit");
        } catch (JSONException e) {
            log.error(e);
            start = 0;
            limit = 0;
        }
        try {
            array = jo.getJSONArray("fields");
            for (int i = 0; i < array.length(); ++i)
                fields.add(array.getJSONObject(i).getString("field"));
        } catch (JSONException e) {
            log.error(e);
            fields = null;
        }
        try {
            array = jo.getJSONArray("keywords");
            for (int i = 0; i < array.length(); ++i)
                keywords.add(array.getJSONObject(i).getString("key"));
        } catch (JSONException e) {
            log.error(e);
            keywords = null;
        }
        try {
            array = jo.getJSONArray("types");
            for (int i = 0; i < array.length(); ++i)
                types.add(array.getJSONObject(i).getString("type"));
        } catch (JSONException e) {
            log.error(e);
            types = null;
        }
        try {
            array = jo.getJSONArray("subtypes");
            for (int i = 0; i < array.length(); ++i)
                subtypes.add(array.getJSONObject(i).getString("subtype"));
        } catch (JSONException e) {
            log.error(e);
            subtypes = null;
        }
        return search(keywords, fields, types, subtypes, start, limit, filter);
    }

    protected String performSearch(CompassSession session, List<String> keywords, List<String> fields,
                                   List<String> types, List<String> subtypes, int start, int limit, String filter) {
        boolean unpaging = true;
        if (limit > 0)
            unpaging = false;
        String result = "";
        CompassQueryBuilder queryBuilder = session.queryBuilder();
        CompassBooleanQueryBuilder boolQuery = queryBuilder.bool();
        CompassBooleanQueryBuilder manualQuery = queryBuilder.bool();
        log.debug("keywords: " + keywords);
        log.debug("type: " + types);
        log.debug("subtype: " + subtypes);
        log.debug("fields: " + fields);
        // first query in manualBind field
        StringBuffer keyBuffer = new StringBuffer();
        if (keywords != null) {
            for (String key : keywords) {
                keyBuffer.append("manualbind: ");
                keyBuffer.append(QueryStringProcess.replaceAllIllegleChar(key));
                keyBuffer.append(" OR ");
            }
            log.debug(keyBuffer.substring(0, keyBuffer.lastIndexOf("OR")));
            manualQuery
                    .addMust(queryBuilder.queryString(keyBuffer.substring(0, keyBuffer.lastIndexOf("OR"))).toQuery());
        }
        if (types != null) {
            StringBuffer typeBuffer = new StringBuffer();
            for (String type : types) {
                typeBuffer.append("type: ");
                typeBuffer.append(QueryStringProcess.replaceAllIllegleChar(type));
                typeBuffer.append(" OR ");
            }
            log.debug(typeBuffer.substring(0, typeBuffer.lastIndexOf("OR")));
            manualQuery.addMust(queryBuilder.queryString(typeBuffer.substring(0, typeBuffer.lastIndexOf("OR")))
                    .toQuery());
            boolQuery
                    .addMust(queryBuilder.queryString(typeBuffer.substring(0, typeBuffer.lastIndexOf("OR"))).toQuery());
        }
        if (subtypes != null) {
            StringBuffer subTypeBuffer = new StringBuffer();
            for (String subtype : subtypes) {
                subTypeBuffer.append("subtype: ");
                subTypeBuffer.append(QueryStringProcess.replaceAllIllegleChar(subtype));
                subTypeBuffer.append(" OR ");
            }
            log.debug(subTypeBuffer.substring(0, subTypeBuffer.lastIndexOf("OR")));
            manualQuery.addMust(queryBuilder.queryString(subTypeBuffer.substring(0, subTypeBuffer.lastIndexOf("OR")))
                    .toQuery());
            boolQuery.addMust(queryBuilder.queryString(subTypeBuffer.substring(0, subTypeBuffer.lastIndexOf("OR")))
                    .toQuery());
        }
        if (filter != null) {
            String[] ft = filter.split("TO");
            if (ft.length != 2)
                log.error("filter is error !");
            else if (ft[0].trim().matches("\\d{4}-\\d{1,2}-\\d{1,2} \\d{1,2}:\\d{1,2}")
                    && ft[1].trim().matches("\\d{4}-\\d{1,2}-\\d{1,2} \\d{1,2}:\\d{1,2}")) {
                manualQuery.addMust(queryBuilder.between("updateTime", ft[0].trim(), ft[1].trim(), true));
                boolQuery.addMust(queryBuilder.between("updateTime", ft[0].trim(), ft[1].trim(), true));
            } else
                log.error("filter is error !");
        }
        CompassHits manualHits = manualQuery.toQuery()
                .setAliases(new String[]{"info", "program", "schedule", "entrance", "series"})
                .addSort("updateTimeMillis", CompassQuery.SortDirection.REVERSE).hits();
        log.debug("人工绑定结果集数量：" + manualHits.length());
        // second query in non-manualBind fields
        String temp = keyBuffer.substring(0, keyBuffer.lastIndexOf("OR"));
        log.debug("addMustNot: " + temp);
        boolQuery.addMustNot(queryBuilder.queryString(temp).toQuery());
        if (fields == null) {
            log.debug("all fields");
            // String temp = keyBuffer.substring(0,keyBuffer.lastIndexOf("OR"));
            // log.debug("addMustNot: " + temp);
            // boolQuery.addMustNot(queryBuilder.queryString(temp).toQuery());
            // temp = temp.replaceAll("manualbind:", "");
            // log.debug("addMust: " + temp);
            // boolQuery.addMust(queryBuilder.queryString(temp).toQuery());

            log.debug("addMust: " + temp.replaceAll("manualbind:", ""));
            boolQuery.addMust(queryBuilder.queryString(temp.replaceAll("manualbind:", "")).toQuery());
        } else {
            // String temp = keyBuffer.substring(0,keyBuffer.lastIndexOf("OR"));
            StringBuffer tb = new StringBuffer();
            for (String field : fields) {
                if (!"manualbind".equals(field)) {
                    tb.append(temp.replaceAll("manualbind", field));
                    tb.append(" OR ");
                }
            }
            log.debug("addMust:" + tb.substring(0, tb.lastIndexOf("OR")));
            boolQuery.addMust(queryBuilder.queryString(tb.substring(0, tb.lastIndexOf("OR"))).toQuery());
        }
        CompassHits hits = boolQuery.toQuery()
                .setAliases(new String[]{"info", "program", "schedule", "entrance", "series"})
                .addSort("updateTimeMillis", CompassQuery.SortDirection.REVERSE).hits();
        log.debug("非人工绑定结果集数量：" + hits.length());
        try {
            if (manualHits.length() + hits.length() == 0)
                return "{\"totalCount\":0}";
            JSONArray array = new JSONArray();
            // 不分页
            if (unpaging) {
                for (int i = 0; i < manualHits.length(); ++i) {
                    Info info = (Info) manualHits.data(i);
                    // log.debug(info.getType().getName() + "#" + info);
                    log.debug(info.getName()
                            + "  updateTime: "
                            + (info.getUpdateTime() == null ? "null" : DateUtil.convertDateToString("yyyy-MM-dd HH:mm",
                            info.getUpdateTime())));
                    JSONObject jo = infoToJsonObject(info);
                    array.put(jo);
                }
                for (int i = 0; i < hits.length(); ++i) {
                    Info info = (Info) hits.data(i);
                    // log.debug(info.getType().getName() + "#" + info);
                    log.debug(info.getName()
                            + "  updateTime: "
                            + (info.getUpdateTime() == null ? "null" : DateUtil.convertDateToString("yyyy-MM-dd HH:mm",
                            info.getUpdateTime())));
                    JSONObject jo = infoToJsonObject(info);
                    array.put(jo);
                }
                return "{\"totalCount\":\"" + (manualHits.length() + hits.length()) + "\",\"results\":"
                        + array.toString() + "}";
            }
            // 1.start + limit <= manualHits.length()
            // 2.manualHits.length() < start + limit <= maualHits.length() +
            // hits.length()
            // 3.maualHits.length() +hit.length < start + limit,start <
            // maualHits.length() + hits.length()
            if (start + limit <= manualHits.length()) {
                // 只要取出人工绑定结果集的 start -- (start+limit)
                for (int i = start; i < start + limit; ++i) {
                    Info info = (Info) manualHits.data(i);
                    // log.debug(info.getType().getName() + "#" + info);
                    log.debug(info.getName()
                            + "  updateTime: "
                            + (info.getUpdateTime() == null ? "null" : DateUtil.convertDateToString("yyyy-MM-dd HH:mm",
                            info.getUpdateTime())));
                    JSONObject jo = infoToJsonObject(info);
                    array.put(jo);
                }
                return "{\"totalCount\":\"" + (manualHits.length() + hits.length()) + "\",\"results\":"
                        + array.toString() + "}";
            } else if (start + limit <= manualHits.length() + hits.length()) // manualHits.length()
            // <
            // start
            // +
            // limit
            // <=
            // maualHits.length()
            // +
            // hits.length()
            {
                // 肯定没有取完数据：
                // 分两种情况，假如start <
                // manualHits.length，那要从manualHits中取manualHits.length()-start条，再从hits中取剩下的limit
                // - (hits.length - start)条
                // 否则，从hits中取出limit条
                if (start < manualHits.length()) {
                    for (int i = start; i < manualHits.length(); ++i) {
                        Info info = (Info) manualHits.data(i);
                        // log.debug(info.getType()==null?"type: null":(info.getType().getName()
                        // + "#" + info));
                        log.debug(info.getName()
                                + "  updateTime: "
                                + (info.getUpdateTime() == null ? "null" : DateUtil.convertDateToString(
                                "yyyy-MM-dd HH:mm", info.getUpdateTime())));
                        JSONObject jo = infoToJsonObject(info);
                        array.put(jo);
                    }
                    for (int i = 0; i < limit + start - manualHits.length(); ++i) {
                        Info info = (Info) hits.data(i);
                        // log.debug(info.getType()==null?"type: null":(info.getType().getName()
                        // + "#" + info));
                        log.debug(info.getName()
                                + "  updateTime: "
                                + (info.getUpdateTime() == null ? "null" : DateUtil.convertDateToString(
                                "yyyy-MM-dd HH:mm", info.getUpdateTime())));
                        JSONObject jo = infoToJsonObject(info);
                        array.put(jo);
                    }
                } else {
                    for (int i = start - manualHits.length(); i < start - manualHits.length() + limit; ++i) {
                        Info info = (Info) hits.data(i);
                        // log.debug(info.getType()==null?"type: null":(info.getType().getName()
                        // + "#" + info));
                        log.debug(info.getName()
                                + "  updateTime: "
                                + (info.getUpdateTime() == null ? "null" : DateUtil.convertDateToString(
                                "yyyy-MM-dd HH:mm", info.getUpdateTime())));
                        JSONObject jo = infoToJsonObject(info);
                        array.put(jo);
                    }
                }
                return "{\"totalCount\":\"" + (manualHits.length() + hits.length()) + "\",\"results\":"
                        + array.toString() + "}";
            } else if (start < manualHits.length() + hits.length())// maualHits.length()
            // +hit.length
            // < start +
            // limit
            {
                if (start < manualHits.length()) {
                    for (int i = start; i < manualHits.length(); ++i) {
                        Info info = (Info) manualHits.data(i);
                        // log.debug(info.getType()==null?"type: null":(info.getType().getName()
                        // + "#" + info));
                        log.debug(info.getName() + "  updateTime: " + info.getUpdateTimeMillis());
                        JSONObject jo = infoToJsonObject(info);
                        array.put(jo);
                    }
                    for (int i = 0; i < hits.length(); ++i) {
                        Info info = (Info) hits.data(i);
                        // log.debug(info.getType()==null?"type: null":(info.getType().getName()
                        // + "#" + info));
                        log.debug(info.getName() + "  updateTime: " + info.getUpdateTimeMillis());
                        JSONObject jo = infoToJsonObject(info);
                        array.put(jo);
                    }
                } else {
                    for (int i = start - manualHits.length(); i < hits.length(); ++i) {
                        Info info = (Info) hits.data(i);
                        // log.debug(info.getType()==null?"type: null":(info.getType().getName()
                        // + "#" + info));
                        log.debug(info.getName() + "  updateTime: " + info.getUpdateTimeMillis());
                        JSONObject jo = infoToJsonObject(info);
                        array.put(jo);
                    }
                }
            } else // start > hits.length + manualHits.length
            {
                return "[超出页数范围]";
            }
            return "{\"totalCount\":\"" + (manualHits.length() + hits.length()) + "\",\"results\":" + array.toString()
                    + "}";
        } catch (JSONException e) {
            log.error(e);
        }
        return result;
    }

    public String search(final List<String> keywords, final List<String> fields, final List<String> types,
                         final List<String> subtypes, final int start, final int limit, final String filter) {
        return getCompassTemplate().execute(new CompassCallback<String>() {
            public String doInCompass(CompassSession session) throws CompassException {
                log.debug("search in compass");
                return performSearch(session, keywords, fields, types, subtypes, start, limit, filter);
            }
        });
    }

    /**
     * Search for Client
     * <p/>
     * public String search(String json){ String keywords; String fields = null;
     * String sortType; List<SearchFilter> filters = null; int start = 0; int
     * limit = 0; JSONObject jo = null; JSONArray filterArray = null; try { jo =
     * new JSONObject(json); } catch (JSONException e1) { log.error(e1); return
     * "error: JSON Wrong"; } try { keywords = jo.getString("keywords"); } catch
     * (JSONException e) { log.error(e); return "error: keywords is wrong"; }
     * if(keywords == null || "".equals(keywords)) return
     * "error: keywords is wrong"; try { fields = jo.getString("fields"); }
     * catch (JSONException e) { fields = null; } try { sortType =
     * jo.getString("order"); } catch (JSONException e) { sortType = "auto"; }
     * try { start = jo.getInt("start"); limit = jo.getInt("limit"); } catch
     * (JSONException e) { start = -1; } try { filterArray =
     * jo.getJSONArray("filters"); } catch (JSONException e) { log.error(e);
     * return "error: JSON Wrong (filters is not correct)";//filters可以为空 }
     * if(filterArray != null && filterArray.length() > 0){ filters = new
     * ArrayList<SearchFilter>(); for(int i = 0; i < filterArray.length();++i){
     * try { JSONObject temp = filterArray.getJSONObject(i); SearchFilter sf =
     * new
     * SearchFilter(temp.getString("filterField"),temp.getString("filterType"
     * ),temp.getString("filterValue")); filters.add(sf); } catch (JSONException
     * e) { log.error(e); return "error: JSON Wrong (filters is not correct)"; }
     * } } return performSearch(keywords,fields,filters,sortType,start,limit); }
     *
     * @SuppressWarnings("unchecked") private String performSearch( final String
     * keywords, final String fields, final
     * List<SearchFilter> filters, final String
     * sortType, final int start,final int
     * limit){ return (String)
     * getCompassTemplate().execute(new
     * CompassCallback(){ public Object
     * doInCompass(CompassSession session) throws
     * CompassException { CompassQueryBuilder
     * queryBuilder = session.queryBuilder();
     * CompassBooleanQueryBuilder boolQuery =
     * queryBuilder.bool();
     * CompassBooleanQueryBuilder keyQuery =
     * queryBuilder.bool(); //
     * log.debug("keywords:" + keywords); //
     * log.debug("fields:" + fields); //
     * log.debug("filters:" + filters); //
     * log.debug("sortType:" + sortType); //
     * log.debug("start:" + start + ", limit: " +
     * limit); //Set search Condition:
     * (keywords,fields) String[] keys =
     * keywords.trim().split("[ ]+");
     * StringBuffer keyBuffer = new
     * StringBuffer(); keyBuffer.append("(");
     * for(int i = 0; i < keys.length; ++i){
     * keyBuffer.append(QueryStringProcess.
     * replaceAllIllegleChar(keys[i])); if(i !=
     * keys.length - 1) keyBuffer.append(" OR ");
     * } keyBuffer.append(")"); if(fields != null
     * && !"".equals(fields)) {
     * keyQuery.addShould
     * (queryBuilder.queryString("manualBind:" +
     * keyBuffer.toString()).toQuery()); String[]
     * fds = fields.split("[ ]+"); for(String fd
     * : fds)
     * keyQuery.addShould(queryBuilder.queryString
     * (fd + ":" +
     * keyBuffer.toString()).toQuery()); } else {
     * keyQuery
     * .addShould(queryBuilder.queryString
     * ("manualBind:" +
     * keyBuffer.toString()).toQuery());
     * keyQuery.
     * addShould(queryBuilder.queryString("name:"
     * + keyBuffer.toString()).toQuery());
     * keyQuery
     * .addShould(queryBuilder.queryString
     * ("description:" +
     * keyBuffer.toString()).toQuery());
     * keyQuery.
     * addShould(queryBuilder.queryString("tag:"
     * + keyBuffer.toString()).toQuery());
     * keyQuery
     * .addShould(queryBuilder.queryString
     * ("keyword:" +
     * keyBuffer.toString()).toQuery());
     * keyQuery.
     * addShould(queryBuilder.queryString
     * ("directors:" +
     * keyBuffer.toString()).toQuery());
     * keyQuery.
     * addShould(queryBuilder.queryString
     * ("actors:" +
     * keyBuffer.toString()).toQuery()); }
     * <p/>
     * boolQuery.addMust(keyQuery.toQuery());
     * //set search condtion: (filters)
     * if(filters != null) { for(SearchFilter
     * ft:filters) {
     * if("or".equalsIgnoreCase(ft.getFilterType
     * ().trim())) {
     * if(!"".equals(ft.getFilterValue())) {
     * StringBuffer valueBuffer = new
     * StringBuffer(); String[] values =
     * ft.getFilterValue().trim().split("[ ]+");
     * valueBuffer.append("("); for(int i = 0; i
     * < values.length; ++i) {
     * valueBuffer.append(values[i]); if(i !=
     * values.length - 1)
     * valueBuffer.append(" OR "); }
     * valueBuffer.append(")");
     * boolQuery.addMust(
     * queryBuilder.queryString(
     * ft.getFilterField().trim() + ":" +
     * valueBuffer.toString()).toQuery()); } }
     * else if("range".equalsIgnoreCase(ft.
     * getFilterType())) {
     * if(!"".equals(ft.getFilterValue())) {
     * String[] rangeValues =
     * ft.getFilterValue().trim().split("TO");
     * if( rangeValues.length == 2) {
     * if("min".equalsIgnoreCase
     * (rangeValues[0].trim())) {
     * if(rangeValues[1].trim().matches(
     * "\\d{4}-\\d{1,2}-\\d{1,2} \\d{1,2}:\\d{1,2}"
     * ))
     * boolQuery.addMust(queryBuilder.le("updateTime"
     * , rangeValues[1].trim())); else
     * log.error("1,range filter error! Time is wrong."
     * ); } else
     * if("max".equalsIgnoreCase(rangeValues
     * [1].trim())) {
     * if(rangeValues[0].trim().matches(
     * "\\d{4}-\\d{1,2}-\\d{1,2} \\d{1,2}:\\d{1,2}"
     * ))
     * boolQuery.addMust(queryBuilder.ge("updateTime"
     * , rangeValues[0].trim())); else
     * log.error("2,range filter error! Time is wrong."
     * ); } else {
     * if(rangeValues[0].trim().matches(
     * "\\d{4}-\\d{1,2}-\\d{1,2} \\d{1,2}:\\d{1,2}"
     * ) && rangeValues[1].trim().matches(
     * "\\d{4}-\\d{1,2}-\\d{1,2} \\d{1,2}:\\d{1,2}"
     * ))
     * boolQuery.addMust(queryBuilder.between("updateTime"
     * , rangeValues[0].trim(),rangeValues[1].
     * trim(), true)); else
     * log.error("3,range filter error! Time is wrong."
     * ); } } else
     * log.error("range filter error!"); } }else
     * if
     * ("not".equalsIgnoreCase(ft.getFilterType(
     * ))) { if(!"".equals(ft.getFilterValue()))
     * { String[] values =
     * ft.getFilterValue().trim().split("[ ]+");
     * for(String notValue : values)
     * boolQuery.addMustNot
     * (queryBuilder.queryString
     * (ft.getFilterField
     * ()+": "+notValue.trim()).toQuery()); } } }
     * } //sort type // long h =
     * System.currentTimeMillis(); //
     * System.out.println
     * ("Analyze search condtion:" + (h - st));
     * CompassHits hits = null; JSONArray array =
     * new JSONArray();
     * if("time".equalsIgnoreCase(sortType)) hits
     * = boolQuery.toQuery().setAliases(new
     * String
     * []{"info","program","schedule","entrance"
     * ,"series"}).addSort("updateTime",
     * CompassQuery
     * .SortDirection.REVERSE).hits(); else hits
     * = boolQuery.toQuery().setAliases(new
     * String
     * []{"info","program","schedule","entrance"
     * ,"series"}).hits(); if( hits.length() ==
     * 0) return
     * "{\"totalCount\":0,\"results\":[]}"; //
     * long time = System.currentTimeMillis() -
     * h; //
     * System.out.println("search hits time:" +
     * time); if(start < 0)//unpage { for(int i =
     * 0; i < hits.length(); ++i) { Info info =
     * (Info) hits.data(i); //
     * log.debug(info.getName() +
     * "  updateTime: " +
     * (info.getUpdateTime()==null
     * ?"null":DateUtil
     * .convertDateToString("yyyy-MM-dd HH:mm",
     * info.getUpdateTime()))); JSONObject jo;
     * try { jo = infoToJsonObject(info);
     * array.put(jo); } catch (JSONException e) {
     * log.error(e); } } return
     * "{\"totalCount\":\"" + hits.length() +
     * "\",\"results\":" + array.toString() +
     * "}"; } else { if(limit <= 0) return
     * "search condition wrong!Limit should > 0";
     * JSONObject jo = new JSONObject(); try{
     * if(start + limit < hits.length()){ for(int
     * i = start; i < start + limit; ++i){ Info
     * info = (Info) hits.data(i); jo =
     * infoToJsonObject(info); array.put(jo); }
     * }else if(start < hits.length()){ for(int i
     * = start;i < hits.length();++i){ Info info
     * = (Info) hits.data(i); jo =
     * infoToJsonObject(info); array.put(jo); }
     * }else { return "[超出页数范围]"; }
     * }catch(JSONException e){ log.error(e); }
     * return "{\"totalCount\":\"" +
     * hits.length() + "\",\"results\":" +
     * array.toString() + "}"; } } }); }
     */
    private JSONObject infoToJsonObject(Info info) throws JSONException {
        JSONObject jo = new JSONObject();
        jo.put("id", info.getId());
        jo.put("name", info.getName());
        jo.put("subtype", info.getSubType() == null ? "" : info.getSubType().getName());
        jo.put("type", info.getType() == null ? "" : info.getType().getName());
        jo.put("p_type", info.getP_type());
        jo.put("updateTime",
                info.getUpdateTime() == null ? "" : DateUtil.convertDateToString("yyyy-MM-dd HH:mm",
                        info.getUpdateTime()));
        Set<Tag> tagSet = info.getTags();
        StringBuffer tagBuffer = new StringBuffer();
        for (Tag t : tagSet)
            tagBuffer.append(t.getTag()).append("  ");
        // jo.put("mediacode", info.getCode());
        jo.put("tags", tagBuffer.toString());
        jo.put("description", info.getDescription() == null ? "" : info.getDescription());
        Set<Keyword> keySet = info.getKeywords();
        StringBuffer keySb = new StringBuffer();
        for (Keyword k : keySet)
            keySb.append(k.getKeyword()).append("  ");
        jo.put("keywords", keySb.toString());
        if (info.getManualBinds().size() > 0) {
            StringBuffer sb = new StringBuffer();
            for (ManualBind mb : info.getManualBinds()) {
                sb.append(mb.getName());
                sb.append(" ");
            }
            jo.put("manualBind", sb.toString());
        }
        if (info instanceof Entrance) {
            Entrance et = (Entrance) info;
            jo.put("url", et.getUrl());
        } else if (info instanceof Program) {
            Program p = (Program) info;
            jo.put("mediacode", p.getCode());
            jo.put("actors", p.getActors() == null ? "" : p.getActors());
            jo.put("directors", p.getDirectors() == null ? "" : p.getDirectors());
        } else if (info instanceof Series) {
            Series p = (Series) info;
            jo.put("mediacode", p.getCode());
            jo.put("actors", p.getActors() == null ? "" : p.getActors());
            jo.put("directors", p.getDirectors() == null ? "" : p.getDirectors());
        } else if (info instanceof Schedule) {
            Schedule p = (Schedule) info;
            jo.put("mediacode", p.getCode());
            jo.put("actors", p.getActors() == null ? "" : p.getActors());
            jo.put("directors", p.getDirectors() == null ? "" : p.getDirectors());
            jo.put("channel", p.getChannel());
            if (p.getEndPlayTime().before(new Date())) {
                jo.put("type", "回看");
                jo.put("startPlayTime", DateUtil.convertDateToString("yyyy-MM-dd HH:mm", p.getStartPlayTime()));
                jo.put("endPlayTime", DateUtil.convertDateToString("yyyy-MM-dd HH:mm", p.getEndPlayTime()));
            } else {
                jo.put("type", "预告");
                jo.put("startPlayTime", DateUtil.convertDateToString("yyyy-MM-dd HH:mm", p.getStartPlayTime()));
                jo.put("endPlayTime", DateUtil.convertDateToString("yyyy-MM-dd HH:mm", p.getEndPlayTime()));
            }
        }
        return jo;
    }
}
